﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Kinect;
using System.Windows;
using System.IO;
using jKinect.Tools;
//http://dotneteers.net/blogs/vbandi/archive/2013/05/03/kinect-interactions-with-wpf-part-iii-demystifying-the-interaction-stream.aspx
using Microsoft.Kinect.Toolkit.Interaction;

namespace jKinect
{
    public class KinectGestureManager
    {
        public KinectManager KinectManager { get; set; }
        public KinectGestureManager()
        {
            KinectManager = new KinectManager();
        }
        public Skeleton CurrentSkeleton = null;
        Skeleton[] skeletons = new Skeleton[6];
        UserInfo[] _userInfos = new UserInfo[InteractionFrame.UserInfoArrayLength];

        public void KinectManager_InteractionFrameReady(object sender, InteractionFrameReadyEventArgs e)
        {
            if (CurrentSkeleton != null)
            {
                using (InteractionFrame frame = e.OpenInteractionFrame())
                {
                    if (frame != null)
                    {
                        frame.CopyInteractionDataTo(_userInfos);
                        UserInfo userInfo = _userInfos.FirstOrDefault(u => u.SkeletonTrackingId == CurrentSkeleton.TrackingId);
                        if (userInfo != null)
                        {
                            if (InteractionFrameReadyHandler != null)
                            {
                                JointsDictionary joints = new JointsDictionary();
                                foreach (Joint joint in CurrentSkeleton.Joints)
                                {
                                    //if (joint.TrackingState != JointTrackingState.NotTracked)
                                    joints.Add(joint.JointType.ToString(), joint.Position);
                                }
                                InteractionFrameReadyHandler(this, new JointsEventArgs(joints, userInfo.HandPointers.ToArray()));
                            }
                        }
                    }
                }
            }
        }

        public void KinectManager_SkeletonFrameReady(object sender, AllFramesReallyReadyEventArgs e)
        {
            if (e.SkeletonFrame == null)
                return;
            if (e.DepthFrame == null)
                return;

            e.SkeletonFrame.CopySkeletonDataTo(skeletons);
            CurrentSkeleton = GetClosestSkeleton(skeletons);
            if (CurrentSkeleton != null)
            {
                if (InteractionFrameReadyHandler != null && CurrentSkeleton.Position.Z < 2.5f)
                {
                    // InteractionStream needs synchronized Skeleton&Depth
                    e.SkeletonFrame.Timestamp = e.DepthFrame.Timestamp;

                    try
                    {
                        if (KinectManager.InteractionStream != null)
                        {
                            var accelerometerReading = KinectManager.Runtime.AccelerometerGetCurrentReading();
                            KinectManager.InteractionStream.ProcessSkeleton(
                            skeletons,
                            accelerometerReading, e.SkeletonFrame.Timestamp);
                        }
                    }
                    catch (InvalidOperationException)
                    {
                        // DepthFrame functions may throw when the sensor gets
                        // into a bad state.  Ignore the frame in that case.
                    }
                  
                    try
                    {
                        KinectManager.InteractionStream.ProcessDepth(e.DepthFrame.GetRawPixelData(), e.DepthFrame.Timestamp);
                    }
                    catch (InvalidOperationException)
                    {
                        // DepthFrame functions may throw when the sensor gets
                        // into a bad state.  Ignore the frame in that case.
                    }
                }
                if (PlayerStatusHandler != null)
                {
                    PlayerStatusHandler(this, new PlayerStatusEventArgs(
                        CurrentSkeleton.TrackingId,
                        CurrentSkeleton.TrackingState.ToString(),
                        CurrentSkeleton.Position,
                        skeletons.Count(s => s.TrackingState != SkeletonTrackingState.NotTracked)
                        ));
                }
                processSkeleton(sender, CurrentSkeleton);
                processSkeletons(sender, skeletons);
            }
            else
            {
                if (PlayerStatusHandler != null)
                {
                    PlayerStatusHandler(this, new PlayerStatusEventArgs(0, SkeletonTrackingState.NotTracked.ToString(), new Microsoft.Kinect.SkeletonPoint(), 0));
                }
            }
        }

        private Skeleton processSkeleton(object sender, Skeleton currentSkeleton)
        {
            if (HeadPositionHandler != null)
            {
                if (CurrentSkeleton.TrackingState == SkeletonTrackingState.Tracked)
                    HeadPositionHandler(sender, new JointEventArgs(CurrentSkeleton.Joints[JointType.Head].Position));
                else
                    HeadPositionHandler(sender, new JointEventArgs(CurrentSkeleton.Position));

            }

            if (CurrentSkeleton.TrackingState == SkeletonTrackingState.Tracked)
            {
                if (SkeletonFrameReadyHandler != null)
                {
                    JointsDictionary joints = new JointsDictionary();
                    foreach (Joint joint in CurrentSkeleton.Joints)
                    {
                        //if (joint.TrackingState != JointTrackingState.NotTracked)
                        joints.Add(joint.JointType.ToString(), joint.Position);
                    }
                    SkeletonFrameReadyHandler(this, new JointsEventArgs(joints, null));
                }
            }
            else //if (skeleton.TrackingState == SkeletonTrackingState.PositionOnly)
            {
            }
            // foreach (Skeleton skeleton in skeletonsTracked)
            //{
            FindGestures(sender, CurrentSkeleton);
            //}

            return currentSkeleton;
        }

        private List<Skeleton> processSkeletons(object sender, Skeleton[] skeletons)
        {
            List<Skeleton> skeletonsTracked = skeletons.Where(sk => sk.TrackingState == SkeletonTrackingState.Tracked).OrderBy(sk => sk.Position.Z).ToList();
            if (AllPlayersJointsHandler != null)
            {
                AllPlayersJointsEventArgs allPlayersEvent = new AllPlayersJointsEventArgs();
                foreach (Skeleton skTracked in skeletonsTracked)
                {
                    JointsDictionary joints = new JointsDictionary();
                    foreach (Joint joint in CurrentSkeleton.Joints)
                    {
                        joints.Add(joint.JointType.ToString(), joint.Position);
                    }
                    allPlayersEvent.AddPlayerJoints(skTracked.TrackingId, joints);
                }
                AllPlayersJointsHandler(this, allPlayersEvent);
            }

            if (AllHeadPositionsHandler != null && skeletonsTracked.Count > 0)
            {
                AllPlayersJointsEventArgs allPlayersEvent = new AllPlayersJointsEventArgs();
                foreach (Skeleton skTracked in skeletonsTracked)
                {
                    JointsDictionary head = new JointsDictionary();
                    head.Add(JointType.Head.ToString(), skTracked.Joints[JointType.Head].Position);
                    allPlayersEvent.AddPlayerJoints(skTracked.TrackingId, head);
                }
                AllHeadPositionsHandler(this, allPlayersEvent);
            }

            return skeletonsTracked;
        }

        private Skeleton GetClosestSkeleton(Skeleton[] skeletons)
        {
            return skeletons.Where(sk => sk.TrackingState != SkeletonTrackingState.NotTracked).OrderBy(sk => sk.Position.Z).FirstOrDefault();
        }

        public event EventHandler<JointsEventArgs> SkeletonFrameReadyHandler;
        public event EventHandler<JointsEventArgs> InteractionFrameReadyHandler;
        public event EventHandler<AllPlayersJointsEventArgs> AllPlayersJointsHandler; // Récupere tous les skeletons tracked
        public event EventHandler<AllPlayersJointsEventArgs> AllHeadPositionsHandler; // Récupere pour tous les joueurs la position de la tête
        public event EventHandler<JointEventArgs> HeadPositionHandler;
        public event EventHandler<JointEventArgs> HandPositionHandler;
        public event EventHandler<PlayerStatusEventArgs> PlayerStatusHandler;
        public event EventHandler<JSONableEventArgs> HandUpHandler;
        public event EventHandler<JSONableEventArgs> HandsUpHandler;
        public event EventHandler<JointsEventArgs> HandsPositionHandler;
        public event EventHandler<JSONableEventArgs> HandJoinedHandler;
        public event EventHandler<ParamsEventArgs> SwipeHandler;
        public event EventHandler<ParamEventArgs> SwipeLeftHandler;
        public event EventHandler<ParamEventArgs> SwipeRightHandler;
        public event EventHandler<ParamsEventArgs> HandsDirectionHandler;
        public event EventHandler<ParamEventArgs> HandDirectionHandler;

        private void FindGestures(object sender, Skeleton skeleton)
        {
            if (skeleton.TrackingState == SkeletonTrackingState.Tracked)
            {
                if (HandPositionHandler != null)
                {
                    Microsoft.Kinect.SkeletonPoint handPosition = DetectAbsoluteHandPosition(skeleton);
                    HandPositionHandler(this, new JointEventArgs(handPosition));
                }
                if (skeleton.Joints[JointType.HandLeft].Position.Y > skeleton.Joints[JointType.Head].Position.Y + .20
                    && skeleton.Joints[JointType.HandRight].Position.Y > skeleton.Joints[JointType.Head].Position.Y + .20)
                {
                    if (HandsUpHandler != null)
                        HandsUpHandler(this, null);
                }
                else
                    if ((skeleton.Joints[JointType.HandLeft].Position.Y > skeleton.Joints[JointType.Head].Position.Y + .20
                        && skeleton.Joints[JointType.HandRight].Position.Y < skeleton.Joints[JointType.Head].Position.Y - .20)

                      || (skeleton.Joints[JointType.HandRight].Position.Y > skeleton.Joints[JointType.Head].Position.Y + .20
                      && skeleton.Joints[JointType.HandLeft].Position.Y < skeleton.Joints[JointType.Head].Position.Y - .20))
                    {
                        if (HandUpHandler != null)
                            HandUpHandler(this, null);
                    }
                if (HandsPositionHandler != null || HandJoinedHandler != null)
                {
                    if (DetectHandsInFront(skeleton))
                    {
                        if (HandsPositionHandler != null)
                        {
                            HandsPositionHandler(this, new JointsEventArgs(new JointsDictionary()
                        {
                            {JointType.HandLeft.ToString(), skeleton.Joints[JointType.HandLeft].Position},
                            {JointType.HandRight.ToString(), skeleton.Joints[JointType.HandRight].Position}
                        }, null));
                        }
                        if (HandJoinedHandler != null)
                            if (DetectJoiningHand(skeleton))
                            {
                                HandJoinedHandler(this, null);
                            }
                    }
                }
                if (HandsDirectionHandler != null)
                {
                    HandDirection handLeft, handRight;
                    handLeft = RecordHandDirection(skeleton, skeleton.Joints[JointType.HandLeft]);
                    handRight = RecordHandDirection(skeleton, skeleton.Joints[JointType.HandRight]);
                    HandsDirectionHandler(this, new ParamsEventArgs(handLeft.ToString(), handRight.ToString()));
                }
                if (SwipeHandler != null || SwipeLeftHandler != null || SwipeRightHandler != null
                    || HandDirectionHandler != null)
                {
                    Joint hand = new Joint();
                    Joint elbow = new Joint();
                    Joint shoulder = new Joint();
                    Joint hip = skeleton.Joints[JointType.HipCenter];
                    HandDirection swipeDirection = HandDirection.None;
                    // find hand
                    if (FindClosestHand(skeleton, ref hand, ref elbow, ref shoulder))
                    {
                        // record
                        swipeDirection = RecordHandDirection(skeleton, hand);
                        if (SwipeHandler != null || SwipeLeftHandler != null || SwipeRightHandler != null)
                        {
                            swipeDirection = DetectHandSwipe(skeleton, hand, elbow, shoulder);
                        }
                    }
                    // no hand
                    else
                    {
                        if (SwipeHandler != null || SwipeLeftHandler != null || SwipeRightHandler != null)
                        {
                            swipeDirection = HandDirection.None;
                        }
                        if (HandDirectionHandler != null)
                        {
                            swipeDirection = HandDirection.None;
                        }
                    }
                    // call handler
                    if (SwipeHandler != null || SwipeLeftHandler != null || SwipeRightHandler != null)
                    {
                        if (swipeDirection != HandDirection.None && SwipeHandler != null)
                            SwipeHandler(this, new ParamsEventArgs(_lastHandDirections[hand.JointType].ConvertAll(new Converter<HandDirection, string>(dir => dir.ToString())).ToArray()));
                        switch (swipeDirection)
                        {
                            case (HandDirection.Left):
                                if (SwipeLeftHandler != null)
                                    SwipeLeftHandler(this, null);
                                break;
                            case (HandDirection.Right):
                                if (SwipeRightHandler != null)
                                    SwipeRightHandler(this, null);
                                break;
                            case (HandDirection.None):
                                break;
                        }
                    }
                    if (HandDirectionHandler != null)
                    {
                        HandDirectionHandler(this, new ParamEventArgs(swipeDirection.ToString()));
                    }
                }
            }
        }

        private Dictionary<JointType, List<HandDirection>> _lastHandDirections = new Dictionary<JointType, List<HandDirection>>()
        {
            {JointType.HandLeft, new List<HandDirection>()},
            {JointType.HandRight, new List<HandDirection>()}
        };
        private HandDirection RecordHandDirection(Skeleton skeleton, Joint hand)
        {
            // horizontal threshold
            float handSideOffset = hand.JointType == JointType.HandLeft ? -.2f : .2f;
            HandDirection handDirection = HandDirection.Unknown;
            if (hand.Position.X < skeleton.Joints[JointType.ShoulderLeft].Position.X + handSideOffset)
                handDirection = HandDirection.Left;
            else if (hand.Position.X > skeleton.Joints[JointType.ShoulderRight].Position.X + handSideOffset)
                handDirection = HandDirection.Right;
            else
                handDirection = HandDirection.Unknown;

            // vertical threshold
            if (hand.Position.Y < SkeletonMath.Barycentric(skeleton.Joints[JointType.ShoulderCenter].Position, skeleton.Joints[JointType.Spine].Position).Y)
                handDirection |= HandDirection.Down;
            else if (hand.Position.Y > skeleton.Joints[JointType.Head].Position.Y + .05)
                handDirection |= HandDirection.Up;

            // inactive position thresholds
            if (hand.Position.Z > skeleton.Joints[JointType.HipCenter].Position.Z - .1f
                && Math.Abs(hand.Position.Y - skeleton.Joints[JointType.HipCenter].Position.Y) < .2f
                && Math.Abs(hand.Position.X - skeleton.Joints[JointType.HipCenter].Position.X) < .4f
                )
            {
                //clear history
                _lastHandDirections[hand.JointType].Clear();
                return HandDirection.None;
            }
            //record history
            if (_lastHandDirections[hand.JointType].Count == 0 || _lastHandDirections[hand.JointType].Last() != handDirection)
                _lastHandDirections[hand.JointType].Add(handDirection);
            return handDirection;
        }

        #region Advanced gestures
        private Smoother _headSmoother = new Smoother();
        private Smoother _handSmoother = new Smoother();

        public bool RelativeMode { get; set; }

        private float _armRaisedLength = .18f;

        public float ArmRaisedLength
        {
            get { return _armRaisedLength; }
            set { _armRaisedLength = value; }
        }

        private float _armExtendedLength = .40f;

        public float ArmExtendedLength
        {
            get { return _armExtendedLength; }
            set { _armExtendedLength = value; }
        }
        private Microsoft.Kinect.SkeletonPoint _lastHandPosition = new Microsoft.Kinect.SkeletonPoint() { X = -200, Y = -200 };

        private bool FindClosestHand(Skeleton skeleton,
            ref Joint hand,
            ref Joint elbow,
            ref Joint shoulder
            )
        {

            if (SkeletonMath.Distance(skeleton.Joints[JointType.HandLeft].Position, skeleton.Joints[JointType.HandRight].Position) < .3
                || Math.Abs(skeleton.Joints[JointType.HandLeft].Position.Z - skeleton.Joints[JointType.HandRight].Position.Z) < ArmRaisedLength / 2
                )

                return false;
            if (skeleton.Joints[JointType.HandLeft].Position.Z < skeleton.Joints[JointType.HandRight].Position.Z)
            {
                elbow = skeleton.Joints[JointType.ElbowLeft];
                hand = skeleton.Joints[JointType.HandLeft];
                shoulder = skeleton.Joints[JointType.ShoulderLeft];
            }
            else
            {
                elbow = skeleton.Joints[JointType.ElbowRight];
                hand = skeleton.Joints[JointType.HandRight];
                shoulder = skeleton.Joints[JointType.ShoulderRight];
            }
            if (hand.Position.Y < skeleton.Joints[JointType.Spine].Position.Y)
                return false;
            // Arm raised above theorical/minimal elbow position
            //System.Diagnostics.Debug.WriteLine(hand.Z + " === " + skeleton.Joints[JointType.Spine].Position.Z + " - " + ArmToHeadDistance);
            if (hand.Position.Y > skeleton.Joints[JointType.Spine].Position.Y)
                if (
                    // hand in front
                    hand.Position.Z < skeleton.Joints[JointType.Spine].Position.Z - ArmRaisedLength
                    //&& Utils.Distance(hand, skeleton.Joints[JointType.Head].Position) > ArmToHeadDistance
                   )
                {
                    return true;
                }
            return false;
        }

        private bool DetectHello(Skeleton skeleton, Microsoft.Kinect.SkeletonPoint elbow)
        {
            return _handSmoother.Any(hand =>
            {
                // Forearm up (around head vertical position)
                if (skeleton.Joints[JointType.Head].Position.Y - .3 < hand.Y && hand.Y < skeleton.Joints[JointType.Head].Position.Y + .3
                    // hand vertically aligned with elbow
                    && elbow.X - 0.1 < hand.X && hand.X < elbow.X + 0.1)
                {
                    return true;
                }
                return false;
            });
        }

        private bool DetectHandPointing(Skeleton skeleton)
        {
            return _handSmoother.All(hand =>
            {
                if (
                    // Forearm up (around head vertical position)
                    skeleton.Joints[JointType.Head].Position.Y - .4 < hand.Y && hand.Y < skeleton.Joints[JointType.Head].Position.Y + .3
                    // Forearm up (around head horizontal position)
                    && skeleton.Joints[JointType.Head].Position.X - .3 < hand.X && hand.X < skeleton.Joints[JointType.Head].Position.X + .3
                    // hand in front
                    && hand.Z < skeleton.Joints[JointType.Spine].Position.Z - ArmRaisedLength
                    && SkeletonMath.Distance(hand, skeleton.Joints[JointType.Head].Position) > ArmRaisedLength
                    )
                {
                    return true;
                }
                return false;
            });
        }

        private Microsoft.Kinect.SkeletonPoint DetectRelativeHandPosition(Skeleton skeleton)
        {
            Joint hand = new Joint();
            Joint elbow = new Joint();
            Joint shoulder = new Joint();
            if (!FindClosestHand(skeleton, ref hand, ref elbow, ref shoulder))
            {
                //not pointing anymore
                _lastHandPosition.X = -200;
                _lastHandPosition.Y = -200;
                return _lastHandPosition;
            }
            _handSmoother.ExponentialWeightedAvg(hand.Position, 10);
            if (_lastHandPosition.X == -200 && _lastHandPosition.Y == -200)
            {
                // Starting position
                if (DetectHandPointing(skeleton))
                {
                    _lastHandPosition.X = hand.Position.X;
                    _lastHandPosition.Y = hand.Position.Y;
                    _lastHandPosition.Z = hand.Position.Z;
                    return hand.Position;
                }
                else
                {
                    // Not a starting position (return -200 vector)
                    return _lastHandPosition;
                }
            }
            else
            {
                return new Microsoft.Kinect.SkeletonPoint()
                {
                    X = hand.Position.X - _lastHandPosition.X,
                    Y = hand.Position.Y - _lastHandPosition.Y,
                    Z = hand.Position.Z - _lastHandPosition.Z
                };
            }
        }

        private Microsoft.Kinect.SkeletonPoint DetectAbsoluteHandPosition(Skeleton skeleton)
        {
            Joint hand = new Joint();
            Joint elbow = new Joint();
            Joint shoulder = new Joint();
            if (!FindClosestHand(skeleton, ref hand, ref elbow, ref shoulder))
                return new Microsoft.Kinect.SkeletonPoint() { X = -200, Y = -200 };
            hand.Position = _handSmoother.ExponentialWeightedAvg(hand.Position, 5);

            Microsoft.Kinect.SkeletonPoint shoudlerLeft = skeleton.Joints[JointType.ShoulderLeft].Position;
            Microsoft.Kinect.SkeletonPoint shoulderRight = skeleton.Joints[JointType.ShoulderRight].Position;
            Microsoft.Kinect.SkeletonPoint hipCenter = skeleton.Joints[JointType.HipCenter].Position;
            Microsoft.Kinect.SkeletonPoint head = skeleton.Joints[JointType.Head].Position;

            //Microsoft.Kinect.SkeletonPoint centerOfVision = Utils.Barycentric(
            //    new Microsoft.Xna.Framework.Vector3[]{
            //        shoudlerLeft.ToVector3D(), 
            //        shoulderRight.ToVector3D(),
            //        head.ToVector3D(),
            //        hipCenter.ToVector3D()
            //    }
            //        ).ToKinectVector();
            //centerOfVision.X += Settings.Default.CenterOfVisionXOffset;
            //centerOfVision.Y += Settings.Default.CenterOfVisionYOffset;
            Microsoft.Kinect.SkeletonPoint centerOfVision = skeleton.Joints[JointType.HipCenter].Position;
            centerOfVision.Y += SkeletonMath.Distance(skeleton.Joints[JointType.HipCenter].Position, skeleton.Joints[JointType.Head].Position);
            Microsoft.Kinect.SkeletonPoint smoothCenterOfVision = _headSmoother.ExponentialWeightedAvg(centerOfVision, 500);

            // arm straight ahead
            //TODO: use angle to find direction->target using centerOfVision-hand
            return hand.Position;
            return new Microsoft.Kinect.SkeletonPoint()
            {
                X = hand.Position.X - smoothCenterOfVision.X,
                Y = hand.Position.Y - smoothCenterOfVision.Y,
                Z = hand.Position.Z - smoothCenterOfVision.Z
            };
            //return new Point(hand.X + Settings.Default.CenterOfVisionXOffset, hand.Y + Settings.Default.CenterOfVisionYOffset);
        }

        public Microsoft.Kinect.SkeletonPoint DetectHandPosition(Skeleton skeleton)
        {
            return RelativeMode ? DetectRelativeHandPosition(skeleton) : DetectAbsoluteHandPosition(skeleton);
        }

        [Flags]
        public enum HandDirection
        {
            None = 0,               //00000000
            Unknown = 1,            //00000001
            Left = 2,               //00000010
            Right = 4,              //00000100
            Up = 8,                 //00001000
            Down = 16,              //00010000
            //UpLeft=10,            //00001010
            //UpRight=12,           //00001100
            //DownRight = 20,       //00010100
            //DownLeft = 18         //00010010
            Horizontal = 32,
            Vertical = 64,
            Slash = 128
        }

        private DateTime _lastSwipeTime = new DateTime();
        public HandDirection DetectHandSwipeOld(
            Skeleton skeleton,
            Joint hand,
            Joint elbow,
            Joint shoulder)
        {
            Joint hip = skeleton.Joints[JointType.HipCenter];

            HandDirection newHandDirection = HandDirection.None;

            Microsoft.Kinect.SkeletonPoint centerOfSwipe = SkeletonMath.Barycentric(
                new Microsoft.Kinect.SkeletonPoint[] { hip.Position, elbow.Position, elbow.Position, elbow.Position });


            //if ( // intersection area between vertical & horizontal
            //    hand.Y > elbow.Y + .1f
            //    && hand.Y < shoulder.Y + .2f
            //    && Math.Abs(hand.X - elbow.X) < 0.1f
            //    )
            //    return HandPosition.Unknown;
            //else
            {
                #region detect horizontal swipe
                if (hand.Position.Y > elbow.Position.Y + .1f
                    && hand.Position.Y < shoulder.Position.Y + .2f
                    && hand.Position.Z < shoulder.Position.Z - 1.2f * ArmRaisedLength
                    )
                {
                    newHandDirection = HandDirection.Unknown;
                    if (hand.Position.X > centerOfSwipe.X + .1)
                    {
                        newHandDirection = HandDirection.Right;
                    }
                    if (hand.Position.X < centerOfSwipe.X - .1)
                    {
                        newHandDirection = HandDirection.Left;
                    }
                }
                #endregion

                #region detect vertical swipe
                if (newHandDirection == HandDirection.None || newHandDirection == HandDirection.Unknown)
                {
                    if (hand.Position.Z < shoulder.Position.Z - ArmExtendedLength
                        && Math.Abs(hand.Position.X - elbow.Position.X) < 0.10)
                    {
                        newHandDirection = HandDirection.Unknown;
                        if (hand.Position.Y > shoulder.Position.Y + 0.25)
                        {
                            newHandDirection = HandDirection.Up;
                        }
                        if (hand.Position.Y < shoulder.Position.Y - 0.1)
                        {
                            newHandDirection = HandDirection.Down;
                        }
                    }
                }
                #endregion
            }
            if (newHandDirection == HandDirection.Unknown)
            {
                return newHandDirection;
            }
            //Console.Write("swipe ? " + _lastHandDirection + " " + newHandDirection + "\t");
            if (_lastHandDirections[hand.JointType].Last() != HandDirection.None)
            {
                // if Swipe
                if (newHandDirection != _lastHandDirections[hand.JointType].Last()
                    //&& (
                    //    // new and last are either both vertical (up is generic direction) or horizontal (left is generic direction)
                    //       (newHandDirection & HandPosition.Up) == (_lastHandDirection & HandPosition.Up)
                    //    || (newHandDirection & HandPosition.Left) == (_lastHandDirection & HandPosition.Left)
                    //    )
                    )
                {
                    _lastHandDirections[hand.JointType].Add(HandDirection.None);
                    _lastSwipeTime = DateTime.Now;
                    return newHandDirection;
                }
                //else //Same direction
                //    Console.WriteLine("KO");
            }
            // Not yet a gesture, just starting position
            else //_lastHandDirection == HandPosition.None
            {
                //Console.WriteLine("Starting");
                // gérer un delay avant la nouvelle gesture pour que la main ait le temps de revenir en place
                DateTime newEventTime = DateTime.Now;
                if ((newEventTime - _lastSwipeTime).TotalSeconds < 1.2)
                {
                    return HandDirection.None;
                }
                else
                {
                    _lastHandDirections[hand.JointType].Add(newHandDirection);
                }
            }
            return newHandDirection;// HandPosition.None;
        }

        public HandDirection DetectHandSwipe(
            Skeleton skeleton,
            Joint hand,
            Joint elbow,
            Joint shoulder)
        {
            if (_lastHandDirections[hand.JointType].Count == 0)
                return HandDirection.None;
            HandDirection newHandDirection = _lastHandDirections[hand.JointType].Last();
            if (_lastHandDirections[hand.JointType].Count >= 3)
            {
                //if (_lastHandDirections[hand.JointType][_lastHandDirections[hand.JointType].Count - 3].HasFlag(HandDirection.Left)
                //    && _lastHandDirections[hand.JointType][_lastHandDirections[hand.JointType].Count - 1].HasFlag(HandDirection.Left)
            }
            //if (_lastHandDirections.Last() != HandDirection.None)
            //{
            //    // if Swipe
            //    if (newHandDirection != _lastHandDirections)
            //    {
            //        _lastHandDirections = HandDirection.None;
            //        _lastSwipeTime = DateTime.Now;
            //        return newHandDirection;
            //    }
            //    //else //Same direction
            //    //    Console.WriteLine("KO");
            //}
            // Not yet a gesture, just starting position
            else //_lastHandDirection == HandPosition.None
            {
                //Console.WriteLine("Starting");
                // gérer un delay avant la nouvelle gesture pour que la main ait le temps de revenir en place
                DateTime newEventTime = DateTime.Now;
                if ((newEventTime - _lastSwipeTime).TotalSeconds < 1.2)
                {
                    return HandDirection.None;
                }
                else
                {
                    //_lastHandDirections.Add(newHandDirection);
                }
            }
            return newHandDirection;// HandPosition.None;
        }
        public bool DetectHandsInFront(Skeleton skeleton)
        {
            Microsoft.Kinect.SkeletonPoint shoulderCenter = skeleton.Joints[JointType.ShoulderCenter].Position;
            Microsoft.Kinect.SkeletonPoint head = skeleton.Joints[JointType.Head].Position;
            Microsoft.Kinect.SkeletonPoint hipCenter = skeleton.Joints[JointType.HipCenter].Position;
            Microsoft.Kinect.SkeletonPoint handLeft = skeleton.Joints[JointType.HandLeft].Position;
            Microsoft.Kinect.SkeletonPoint handRight = skeleton.Joints[JointType.HandRight].Position;
            if (
                // Hands in front
               handLeft.Z < shoulderCenter.Z - 0.2 && handRight.Z < shoulderCenter.Z - 0.2
                // Hands above elbows
               && handLeft.Y > hipCenter.Y && handRight.Y > hipCenter.Y
                // hands below shoulder
               && handLeft.Y < head.Y && handRight.Y < head.Y
               )
            {
                return true;
            }
            return false;
        }

        public bool DetectJoiningHand(Skeleton skeleton)
        {
            Microsoft.Kinect.SkeletonPoint hipCenter = skeleton.Joints[JointType.HipCenter].Position;
            Microsoft.Kinect.SkeletonPoint handLeft = skeleton.Joints[JointType.HandLeft].Position;
            Microsoft.Kinect.SkeletonPoint handRight = skeleton.Joints[JointType.HandRight].Position;

            if (
                handLeft.X > hipCenter.X - .3 && handRight.X < hipCenter.X + .3
                // hands joined
                && Math.Abs(handLeft.X - handRight.X) < .1
                && Math.Abs(handLeft.Y - handRight.Y) < .1
                && Math.Abs(handLeft.Z - handRight.Z) < .1)
            {
                return true;
            }

            return false;
        }

        #endregion

        internal void GetNextSkeletonFrame()
        {
            if (KinectManager.Runtime == null) return;
            SkeletonFrame skeletonFrame = null;
            try
            {
                skeletonFrame = KinectManager.Runtime.SkeletonStream.OpenNextFrame(0);
            }
            catch (Exception ex)
            { }
            DepthImageFrame depthFrame = null;
            try
            {
                depthFrame = KinectManager.Runtime.DepthStream.OpenNextFrame(0);
            }
            catch (Exception ex)
            {
            }
            KinectManager_SkeletonFrameReady(this, new AllFramesReallyReadyEventArgs(skeletonFrame, depthFrame));
            if (skeletonFrame != null)
                skeletonFrame.Dispose();
            if (depthFrame != null)
                depthFrame.Dispose();
        }
    }
}
